#include <utility>

#include "Callback_Handler.h"

#define CALLBACK_TEMPLATE_PARAMS   CALLBACK_MAKE_PARAMS( CALLBACK_PARAMS_NUM, class T )
#define CALLBACK_TEMPLATE_ARGS	   CALLBACK_MAKE_PARAMS( CALLBACK_PARAMS_NUM, T )

#define CALLBACK_TEMPLATE_FUNC_PARAMS	CALLBACK_MAKE_FUNC_PARAMS( CALLBACK_PARAMS_NUM, T, param )
#define CALLBACK_TEMPLATE_FUNC_ARGS		CALLBACK_MAKE_PARAMS( CALLBACK_PARAMS_NUM, param )

#define CALLBACK_VARS				CALLBACK_MAKE_VARS( CALLBACK_PARAMS_NUM, T, arg_ )
#define CALLBACK_VARS_STRUCTURE		CALLBACK_MAKE_VARS_STRUCTURE( CALLBACK_PARAMS_NUM, arg_, param )
#define CALLBACK_VARS_PARAMS		CALLBACK_MAKE_PARAMS( CALLBACK_PARAMS_NUM, arg_ )


#if CALLBACK_PARAMS_NUM == 0
#define CALLBACK_COMMA
#else
#define CALLBACK_COMMA ,
#endif

namespace share
{
template< class R CALLBACK_COMMA CALLBACK_TEMPLATE_PARAMS >
class Callback< R( CALLBACK_TEMPLATE_ARGS ) >
{
public:
	Callback( void );
public:
	~Callback( void );

	R operator( )( CALLBACK_TEMPLATE_FUNC_PARAMS )
	{
		return callback_impl_->invoke( CALLBACK_TEMPLATE_FUNC_ARGS );
	}

	Callback( const Callback & callback )
	{
		if( this != &callback )
		{
			callback_impl_ = callback.callback_impl_->clone( );
		}
	}

	Callback & operator = ( const Callback & callback )
	{
		if( this != &callback )
		{
			callback_impl_ = callback.callback_impl_->clone( );
		}

		return *this;
	}

	template< class TOBJPTR, class TOBJ_FUNC_PTR >
	Callback( const TOBJPTR &obj, const TOBJ_FUNC_PTR & obj_func_ptr );

	template <class TFUNC >
	Callback( const TFUNC& func );

	template <class TFUNC >
	void Set( const TFUNC& func );

	template< class TOBJPTR, class TOBJ_FUNC_PTR >
	void Set( const TOBJPTR &obj, const TOBJ_FUNC_PTR & obj_func_ptr );

private:
	class CallbackImplBase
	{
	public:
		virtual R invoke( CALLBACK_TEMPLATE_FUNC_PARAMS ) = 0;
		virtual CallbackImplBase* clone( ) const = 0;
	};

	template < class TFUNC >
	struct Invoker
	{
		static R invoke( const TFUNC & f CALLBACK_COMMA CALLBACK_TEMPLATE_FUNC_PARAMS )
		{
			return ( const_cast<TFUNC&>( f ) )( CALLBACK_TEMPLATE_FUNC_ARGS );
		}
	};
	template< class TOBJPTR, class TOBJ_FUNC_PTR >
	struct Invoker< std::pair< TOBJPTR,TOBJ_FUNC_PTR > >
	{
		static R invoke( const std::pair< TOBJPTR, TOBJ_FUNC_PTR > &f CALLBACK_COMMA CALLBACK_TEMPLATE_FUNC_PARAMS )
		{
			return (( f.first->*f.second )( CALLBACK_TEMPLATE_FUNC_ARGS ) );
		}
	};
	template < class TFUNC >
	class CallbackImpl : public CallbackImplBase
	{
	public:
		CallbackImpl( const TFUNC & f )
			:func_( f )
		{
		}
		CallbackImpl( const CallbackImpl & impl )
			:func_( impl.func_ )
		{

		}
		virtual R invoke( CALLBACK_TEMPLATE_FUNC_PARAMS )
		{
			return Invoker<TFUNC>::invoke( this->func_ CALLBACK_COMMA CALLBACK_TEMPLATE_FUNC_ARGS );
		}
		virtual CallbackImplBase* clone( ) const
		{
			return new CallbackImpl( *this );
		}

		const TFUNC & func( ) const
		{
			return this->func_;
		}
	private:
		TFUNC func_;
	};
	CallbackImplBase *callback_impl_;
};


template< class R CALLBACK_COMMA CALLBACK_TEMPLATE_PARAMS >
Callback<R( CALLBACK_TEMPLATE_ARGS ) >::Callback( void )
	: callback_impl_( 0 )
{

}

template< class R CALLBACK_COMMA CALLBACK_TEMPLATE_PARAMS >
Callback<R( CALLBACK_TEMPLATE_ARGS ) >::~Callback( void )
{
	if( callback_impl_ )
		delete callback_impl_;

	callback_impl_ = 0;
}

template< class R CALLBACK_COMMA CALLBACK_TEMPLATE_PARAMS >
template< class TFUNC >
Callback< R( CALLBACK_TEMPLATE_ARGS ) >::Callback( const TFUNC & func )
{
	callback_impl_ =
	    new CallbackImpl<TFUNC> ( func );
}

template< class R CALLBACK_COMMA CALLBACK_TEMPLATE_PARAMS >
template< class TOBJPTR, class TOBJ_FUNC_PTR >
Callback<R( CALLBACK_TEMPLATE_ARGS ) >::Callback( const TOBJPTR &obj
        , const TOBJ_FUNC_PTR & func )
{
	Set( std::make_pair( obj, func ) );
}


template< class R CALLBACK_COMMA CALLBACK_TEMPLATE_PARAMS >
template< class TFUNC >
void Callback< R( CALLBACK_TEMPLATE_ARGS ) >::Set( const TFUNC & func )
{
	callback_impl_ =
	    new CallbackImpl<TFUNC> ( func );
}

template< class R CALLBACK_COMMA CALLBACK_TEMPLATE_PARAMS >
template< class TOBJPTR, class TOBJ_FUNC_PTR >
void Callback<R( CALLBACK_TEMPLATE_ARGS ) >::Set( const TOBJPTR &obj
        , const TOBJ_FUNC_PTR & func )
{
	Set( std::make_pair( obj, func ) );
}


template< class R CALLBACK_COMMA CALLBACK_TEMPLATE_PARAMS >
class Callbacker< R( CALLBACK_TEMPLATE_ARGS ) >
{
public:
	template< class TOBJPTR, class TOBJ_FUNC_PTR >
	Callbacker( const TOBJPTR &obj, const TOBJ_FUNC_PTR & obj_func_ptr CALLBACK_COMMA  CALLBACK_TEMPLATE_FUNC_PARAMS )
		: callback_( obj, obj_func_ptr ) CALLBACK_COMMA CALLBACK_VARS_STRUCTURE
	{
	}

	template <class TFUNC >
	Callbacker( const TFUNC& func CALLBACK_COMMA CALLBACK_TEMPLATE_FUNC_PARAMS )
		: callback_( func ) CALLBACK_COMMA CALLBACK_VARS_STRUCTURE
	{
		CALLBACK_MAKE_VARS_ASSIGN( CALLBACK_PARAMS_NUM, arg_, param );
	}

	Callbacker( const Callbacker & callbacker )
	{
		callback_ = callbacker.callback_;
		CALLBACK_MAKE_VARS_ASSIGN( CALLBACK_PARAMS_NUM, arg_, callbacker.arg_ );
	}


	R operator( )( )
	{
		return callback_( CALLBACK_VARS_PARAMS );
	}

private:
	Callback< R( CALLBACK_TEMPLATE_ARGS ) > callback_;
	CALLBACK_VARS;
};

template< class TOBJ, class R CALLBACK_COMMA CALLBACK_TEMPLATE_PARAMS >
CallbackHandlerBase * //< Callbacker< R (CALLBACK_TEMPLATE_ARGS) > >
bind_handler( TOBJ *objptr, R( TOBJ::*obj_func_ptr )( CALLBACK_TEMPLATE_ARGS ) CALLBACK_COMMA  CALLBACK_TEMPLATE_FUNC_PARAMS )
{
	return new CallbackHandler< Callbacker< R( CALLBACK_TEMPLATE_ARGS ) > > ( Callbacker< R( CALLBACK_TEMPLATE_ARGS ) > ( objptr, obj_func_ptr CALLBACK_COMMA CALLBACK_TEMPLATE_FUNC_ARGS ) );
}

template< class R CALLBACK_COMMA CALLBACK_TEMPLATE_PARAMS >
CallbackHandlerBase* //< Callbacker< R (CALLBACK_TEMPLATE_ARGS) > >
bind_handler( R( *func )( CALLBACK_TEMPLATE_ARGS )  CALLBACK_COMMA CALLBACK_TEMPLATE_FUNC_PARAMS )
{
	return new CallbackHandler< Callbacker< R( CALLBACK_TEMPLATE_ARGS ) > > ( Callbacker< R( CALLBACK_TEMPLATE_ARGS ) > ( func CALLBACK_COMMA CALLBACK_TEMPLATE_FUNC_ARGS ) );
}

}

#undef  CALLBACK_TEMPLATE_PARAMS
#undef  CALLBACK_TEMPLATE_ARGS
#undef	CALLBACK_TEMPLATE_FUNC_PARAMS
#undef	CALLBACK_TEMPLATE_FUNC_ARGS
#undef  CALLBACK_COMMA
#undef  CALLBACK_VARS
#undef  CALLBACK_VARS_STRUCTURE
#undef	CALLBACK_VARS_PARAMS


//
// #ifndef __CALLBACK_TEMPLATE_CPP
// #include "Callback_template.cpp"
// #endif